#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>

using namespace std;

int g_cap = 100;

template <class T>
class blockQueue
{
public:
    blockQueue(int capacity = g_cap)
    : _capacity(capacity)
    {
        pthread_mutex_init(&_lock, nullptr);
        pthread_cond_init(&_pcond, nullptr);
        pthread_cond_init(&_ccond, nullptr);
    }
    
    ~blockQueue()
    {
        pthread_mutex_destroy(&_lock);
        pthread_cond_destroy(&_pcond);
        pthread_cond_destroy(&_ccond);
    }

    bool full()
    {
        return _q.size() >= _capacity;
    }

    bool empty()
    {
        return _q.empty();
    }

    void push(const T& in)
    {
        pthread_mutex_lock(&_lock);
        //用while进行循环判断是为了防止多生产线程同时被唤醒 一个线程生产数据后队列已满 其他线程不做full判断继续生产的情况
        while (full())//队列已满 不满足生产条件
            pthread_cond_wait(&_pcond, &_lock);//生产者线程在对应的条件变量上等待
        _q.push(in);
        //队列一定不为空 可以唤醒消费者线程取走数据
        pthread_cond_signal(&_ccond);//唤醒在解锁前 可以提前让消费者线程参与到锁的竞争中
        pthread_mutex_unlock(&_lock);
    }

    void pop(T* out)
    {
        pthread_mutex_lock(&_lock);
        while (empty())//队列为空 不满足消费条件
            pthread_cond_wait(&_ccond, &_lock);
        *out = _q.front();
        _q.pop();
        //队列一定不为满 可以唤醒生产者线程生产数据
        pthread_cond_signal(&_pcond);
        pthread_mutex_unlock(&_lock);
    }
public:
    queue<T> _q;
    int _capacity;
    pthread_mutex_t _lock;
    pthread_cond_t _pcond;
    pthread_cond_t _ccond;
};

template <class C, class S>
struct blockQueues
{
    blockQueues()
    : _cbq(new blockQueue<C>),
      _sbq(new blockQueue<S>)
    {}

    ~blockQueues()
    {
        delete _cbq;
        delete _sbq;
    }

    blockQueue<C>* _cbq;//计算队列
    blockQueue<S>* _sbq;//保存队列
};